package DesignPatern.A06_代理模式.MSB.v10;


import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.TimeUnit;

/**
 * 问题：我想记录坦克的移动时间
 * 最简单的办法：修改代码，记录时间
 * 问题2：如果无法改变方法源码呢？
 * 用继承？
 * v05:使用代理
 * v06:代理有各种类型
 * 问题：如何实现代理的各种组合？继承？Decorator?
 * v07:代理的对象改成Movable类型-越来越像decorator了
 * v08:如果有stop方法需要代理...
 * 如果想让LogProxy可以重用，不仅可以代理Tank，还可以代理任何其他可以代理的类型
 * （毕竟日志记录，时间计算是很多方法都需要的东西），这时该怎么做呢？
 * 分离代理行为与被代理对象
 * 使用jdk的动态代理
 * <p>
 * v09: 横切代码与业务逻辑代码分离 AOP
 * v10: 通过反射观察生成的代理对象
 * jdk反射生成代理必须面向接口，这是由Proxy的内部实现决定的
 */
public class Tank implements Movable
{
    
    /**
     * 模拟坦克移动了一段儿时间
     */
    @Override
    public void move()
    {
        System.out.println("Tank moving claclacla...");
        try
        {
            Thread.sleep(TimeUnit.SECONDS.toSeconds(1));
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args)
    {
        Tank tank = new Tank();
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        TimeProxy timeProxy = new TimeProxy(tank);
        Movable m = (Movable) Proxy.newProxyInstance(
                Tank.class.getClassLoader(),
                Tank.class.getInterfaces(),
                //new Class[]{Movable.class}, //tank.class.getInterfaces()
                timeProxy
        );
        m.move();
    }
}

class TimeProxy implements InvocationHandler
{
    Movable m;
    
    public TimeProxy(Movable m)
    {
        this.m = m;
    }
    
    public void before()
    {
        System.out.println("method start..");
    }
    
    public void after()
    {
        System.out.println("method stop..");
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        //Arrays.stream(proxy.getClass().getMethods()).map(Method::getName).forEach(System.out::println);
        before();
        Object o = method.invoke(m, args);
        after();
        return o;
    }
    
}

interface Movable
{
    void move();
}